/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cctype>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <map>
#include <string>
#define LOG_TAG "DefaultVehicleHal_v2_0"

#include <android-base/macros.h>
#include <android-base/parsedouble.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <vhal_v2_0/Obd2SensorStore.h>

#include "EmulatedVehicleHal.h"
#include "JsonFakeValueGenerator.h"
#include "LinearFakeValueGenerator.h"
#include "VHalLog.h"

namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {

namespace impl {

using namespace ::android::hardware::automotive::vehicle::V2_0;
using android::base::EqualsIgnoreCase;

#define POWER_STATE_MONITOR_PATH "/sys/power/state"

static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorIntegerSensors,
                                                             size_t numVendorFloatSensors) {
    std::unique_ptr<Obd2SensorStore> sensorStore(
        new Obd2SensorStore(numVendorIntegerSensors, numVendorFloatSensors));

    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
                                  toInt(Obd2FuelSystemStatus::CLOSED_LOOP));
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON, 0);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_MONITORS_SUPPORTED,
                                  toInt(Obd2IgnitionMonitorKind::SPARK));
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_SPECIFIC_MONITORS,
                                  Obd2CommonIgnitionMonitors::COMPONENTS_AVAILABLE |
                                      Obd2CommonIgnitionMonitors::MISFIRE_AVAILABLE |
                                      Obd2SparkIgnitionMonitors::AC_REFRIGERANT_AVAILABLE |
                                      Obd2SparkIgnitionMonitors::EVAPORATIVE_SYSTEM_AVAILABLE);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::INTAKE_AIR_TEMPERATURE, 35);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::COMMANDED_SECONDARY_AIR_STATUS,
                                  toInt(Obd2SecondaryAirStatus::FROM_OUTSIDE_OR_OFF));
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT, 1);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::RUNTIME_SINCE_ENGINE_START, 500);
    sensorStore->setIntegerSensor(
        DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON, 0);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::WARMUPS_SINCE_CODES_CLEARED, 51);
    sensorStore->setIntegerSensor(
        DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_SINCE_CODES_CLEARED, 365);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE, 30);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::CONTROL_MODULE_VOLTAGE, 12);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::AMBIENT_AIR_TEMPERATURE, 18);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MAX_FUEL_AIR_EQUIVALENCE_RATIO, 1);
    sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_TYPE,
                                  toInt(Obd2FuelType::GASOLINE));
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD, 0.153);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1, -0.16);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1, -0.16);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK2, -0.16);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK2, -0.16);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE, 7.5);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ENGINE_RPM, 1250.);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::VEHICLE_SPEED, 40.);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::TIMING_ADVANCE, 2.5);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::THROTTLE_POSITION, 19.75);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::OXYGEN_SENSOR1_VOLTAGE, 0.265);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::FUEL_TANK_LEVEL_INPUT, 0.824);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::EVAPORATION_SYSTEM_VAPOR_PRESSURE,
                                -0.373);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CATALYST_TEMPERATURE_BANK1_SENSOR1,
                                190.);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::RELATIVE_THROTTLE_POSITION, 3.);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ABSOLUTE_THROTTLE_POSITION_B, 0.306);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_D, 0.188);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_E, 0.094);
    sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024);

    return sensorStore;
}

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
                                       VehicleHalServer* server,
                                       ecarx::vhal::core::interface::IVendorVehicleServer* vendorImpl,
                                       EmulatedUserHal* emulatedUserHal)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      mRecurrentTimer(
          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
      mVehicleClient(client),
      mEmulatedUserHal(emulatedUserHal),
      mVendorServer(vendorImpl),
      mVehicleHalServer(server) {
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
        mPropStore->registerProperty(kVehicleProperties[i].config);
    }
    auto configs = mVendorServer->onGetAllPropertyConfig();
    for (auto& it : configs) {
        mPropStore->registerProperty(it.config);
    }
    mVehicleClient->registerPropertyValueCallback(std::bind(
        &EmulatedVehicleHal::onPropertyValue, this, std::placeholders::_1, std::placeholders::_2));
}

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(const VehiclePropValue& requestedPropValue,
                                                        StatusCode* outStatus) {
    auto propId = requestedPropValue.prop;
    VHALOG.d("get(%d)", propId);

    auto& pool = *getValuePool();
    VehiclePropValuePtr v = nullptr;

    switch (propId) {
        case OBD2_FREEZE_FRAME:
            v = pool.obtainComplex();
            *outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
            break;
        case OBD2_FREEZE_FRAME_INFO:
            v = pool.obtainComplex();
            *outStatus = fillObd2DtcInfo(v.get());
            break;
        default:
            if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
                VHALOG.d("get(): getting value for prop %d from User HAL", propId);
                const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
                if (!ret.ok()) {
                    VHALOG.e("get(): User HAL returned error: %s", ret.error().message().c_str());
                    *outStatus = StatusCode(ret.error().code());
                } else {
                    auto value = ret.value().get();
                    if (value != nullptr) {
                        VHALOG.d("get(): User HAL returned value: %s", toString(*value).c_str());
                        v = getValuePool()->obtain(*value);
                        *outStatus = StatusCode::OK;
                    } else {
                        VHALOG.e("get(): User HAL returned null value");
                        *outStatus = StatusCode::INTERNAL_ERROR;
                    }
                }
                break;
            }

            auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
            if (internalPropValue != nullptr) {
                v = getValuePool()->obtain(*internalPropValue);
            }
            if (v != nullptr) {
                auto cfg = mPropStore->getConfigOrNull(propId);
                if (cfg!=nullptr) {
                    if (cfg->changeMode == VehiclePropertyChangeMode::STATIC && v->status != VehiclePropertyStatus::AVAILABLE) {
                        *outStatus = StatusCode::TRY_AGAIN;
                    } else {
                        *outStatus = StatusCode::OK;
                    }
                } else {
                    *outStatus = StatusCode::OK;
                }

            } else {
                *outStatus = StatusCode::INVALID_ARG;
            }
            break;
    }
    if (v.get()) {
        v->timestamp = elapsedRealtimeNano();
    }
    return v;
}

bool EmulatedVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
    //创建一个Map<string propName,VehiclePropConfig prop>
    //创建一个get的Map 和 一个set的Map
    std::map<std::string, VehiclePropConfig> propMapRead;
    std::map<std::string, VehiclePropConfig> propMapWrite;
    for (const auto& prop : listProperties()) {
        if (prop.access == VehiclePropertyAccess::READ ||
            prop.access == VehiclePropertyAccess::READ_WRITE) {
            if (isSystemProperty(prop.prop)) {
                propMapRead[toString(static_cast<VehicleProperty>(prop.prop))] = prop;
            } else {
                propMapRead[prop.configString] = prop;
            }
        }
        if (prop.access == VehiclePropertyAccess::WRITE ||
                   prop.access == VehiclePropertyAccess::READ_WRITE) {
            if (isSystemProperty(prop.prop)) {
                propMapWrite[toString(static_cast<VehicleProperty>(prop.prop))] = prop;
            } else {
                propMapWrite[prop.configString] = prop;
            }
        }
    }

    //实现Ecarx-vehicleHal-tool
    if (options.size() == 0) {
        cmdListAllProperties(fd->data[0], propMapRead);
        return false;
    }

    std::string option = options[0];
    if (EqualsIgnoreCase(option, "--help")) {
        cmdHelp(fd->data[0]);
    } else if (EqualsIgnoreCase(option, "--list")) {
        cmdListAllProperties(fd->data[0], propMapRead);
    } else if (EqualsIgnoreCase(option, "--get")) {
        cmdDumpSpecificProperties(fd->data[0], options, propMapRead);
    } else if (EqualsIgnoreCase(option, "--set")) {
        cmdSetOneProperty(fd->data[0], options, propMapWrite);
    } else {
        mVehicleClient->dump(fd, options);
    }
    return false;
}

void EmulatedVehicleHal::cmdHelp(int fd) {
    dprintf(fd, "Usage: \n\n");
    dprintf(fd, "--help: shows this help\n");
    dprintf(fd, "--list: lists the ids of all supported properties\n");
    dprintf(fd,
            "--get <PROPNAME1> [PROPNAME2] [PROPNAMEN]: dumps the value of specific properties \n");
    dprintf(fd,
            "--set <PROPNAME> <i|s|f|b|l> <VALUE_1> [<i|s|f|b|l> <VALUE_N>] [a AREA]] : sets the "
            "value of "
            "property PROP, using arbitrary number of key/value parameters (i for int32, "
            "s for string, f for float, b for byte, l for int64) and an optional area.\n"
            "Notice that the string value can be set just once, while the other can have multiple "
            "values (so they're used in the respective array)\n");
    dprintf(fd,
            "--get_sig <SIGNAME1> [SIGNAME2] [SIGNAMEN]: dumps the value of specific signals \n");
    dprintf(fd, "--set_sig <SIGNAME> <i> <VALUE>: set the value of signal\n");
}

void EmulatedVehicleHal::cmdListAllProperties(int fd,
                                              std::map<std::string, VehiclePropConfig> propMap) {
    for (std::map<std::string, VehiclePropConfig>::iterator it = propMap.begin();
         it != propMap.end(); it++) {
        dprintf(fd, "%s (0x%x): \n", it->first.c_str(), it->second.prop);
        cmdDumpOneProperty(fd, it->second);
    }
}

void EmulatedVehicleHal::cmdDumpOneProperty(int fd, const VehiclePropConfig& config) {
    size_t numberAreas = config.areaConfigs.size();
    if (numberAreas == 0) {
        cmdDumpOneProperty(fd, config.prop, 0);
        return;
    }
    for (size_t j = 0; j < numberAreas; j++) {
        cmdDumpOneProperty(fd, config.prop, config.areaConfigs[j].areaId);
    }
}

void EmulatedVehicleHal::cmdDumpOneProperty(int fd, int32_t prop, int32_t areaId) {
    VehiclePropValue propVal;
    propVal.prop = prop;
    propVal.areaId = areaId;
    StatusCode status;
    auto value=get(propVal, &status);
    if (status != StatusCode::OK) {
        dprintf(fd, "   Could not get property. Error: %s\n", toString(status).c_str());
    } else {
        //判断area
        dprintf(fd, "    area: %s \n", parsingAreaId(prop, areaId).c_str());
        //判断该propID,在不在NOT_AVAILABLE_PROPERTIES中
        if(value->status == VehiclePropertyStatus::UNAVAILABLE && isNotAvailable(fd, prop, areaId)){
            dprintf(fd, "    status: NOTAVAILABLE \n");
        }else {
            dprintf(fd, "    status: %s \n", toString(value->status).c_str());
        }

        dprintf(fd, "    value: %s \n", toString(value->value).c_str());
        dprintf(fd, "    timestamp: %lld \n",(long long)(value->timestamp));
    }
}

std::string EmulatedVehicleHal::parsingAreaId(int32_t propId, int32_t areaId) {
    std::string ret = "";
    if (isGlobalProp(propId)) {
        ret = "GLOBAL";
    } else {
        if (getPropArea(propId) == VehicleArea::WINDOW) {
            // windowtype
            ret = "VehicleAreaWindow::" + toString<VehicleAreaWindow>(areaId);
        } else if (getPropArea(propId) == VehicleArea::MIRROR) {
            // mirrortype
            ret = "VehicleAreaMirror::" + toString<VehicleAreaMirror>(areaId);
        } else if (getPropArea(propId) == VehicleArea::SEAT) {
            // seattype
            ret = "VehicleAreaSeat::" + toString<VehicleAreaSeat>(areaId);
        } else if (getPropArea(propId) == VehicleArea::DOOR) {
            // doortype
            ret = "VehicleAreaDoor::" + toString<VehicleAreaDoor>(areaId);
        } else if (getPropArea(propId) == VehicleArea::WHEEL) {
            // wheeltype
            ret = "VehicleAreaWheel::" + toString<VehicleAreaWheel>(areaId);
        }
    }
    return ret;
}

bool EmulatedVehicleHal::isNotAvailable(int fd, int32_t propId, int32_t areaId) {
    bool ret = false;
    //获取NOT_AVAILABLE_PROPERTIES的值
    VehiclePropValue propVal;
    constexpr const int32_t NOT_AVAILABLE_PROPERTIES = 0xFFFF
                                                |VehiclePropertyGroup::VENDOR
                                                |VehiclePropertyType::INT64_VEC
                                                |VehicleArea::GLOBAL;
    propVal.prop = NOT_AVAILABLE_PROPERTIES;
    propVal.areaId = 0;
    StatusCode status;
    get(propVal, &status);
    if (status == StatusCode::OK) {
        std::vector<int64_t> notAvaPropVal = propVal.value.int64Values;
        //拼接要确认的propId和areaId，areaId高32位，propId低32位
        int64_t propVal = areaId;
        propVal = (propVal << 32);
        propVal |= propId;
        //判断是否在NOT_AVAILABLE_PROPERTIES中
        if (std::find(notAvaPropVal.begin(), notAvaPropVal.end(), propVal) != notAvaPropVal.end()) {
            ret = true;
        }
    } else {
        dprintf(fd, "   get NOT_AVAILABLE_PROPERTIES failed. Error: %s\n", toString(status).c_str());
    }
    return ret;
}

void EmulatedVehicleHal::cmdDumpSpecificProperties(int fd, const hidl_vec<hidl_string>& options,
                                                   std::map<std::string, VehiclePropConfig> propMap) {
    if (!checkArgumentsSize(fd, options, 2)) return;
    size_t size = options.size();
    for (size_t i = 1; i < size; ++i) {
        std::string propName = options[i];
        for (std::map<std::string, VehiclePropConfig>::iterator it = propMap.begin();
             it != propMap.end(); it++) {
            if (EqualsIgnoreCase(it->first, propName)) {
                //然后调用mPropStore->getConfigOrNull，获取VehiclePropConfig
                const VehiclePropConfig* config = mPropStore->getConfigOrNull(it->second.prop);
                dprintf(fd, "%s (0x%x): \n", it->first.c_str(), it->second.prop);
                //调用cmdDumpOneProperty方法进行打印
                cmdDumpOneProperty(fd, *config);
                break;
            }
            if (it == --propMap.end()) {
                dprintf(fd, "Not found %s, please check!!!\n", propName.c_str());
                return;
            }
        }
    }
}

void EmulatedVehicleHal::cmdSetOneProperty(int fd, const hidl_vec<hidl_string>& options,
                                           std::map<std::string, VehiclePropConfig> propMap) {
    if (!checkArgumentsSize(fd, options, 2)) return;
    size_t size = options.size();

    // Syntax is --set PROP Type1 Value1 TypeN ValueN, so number of arguments must be even
    if (size % 2 != 0) {
        dprintf(fd, "must pass even number of arguments (passed %zu)\n", size);
        return;
    }
    int numberValues = (size - 2) / 2;
    VehiclePropValue prop;
    //根据propName,来找到propid
    std::string propName = options[1];
    for (std::map<std::string, VehiclePropConfig>::iterator it = propMap.begin();
         it != propMap.end(); it++) {
        if (EqualsIgnoreCase(it->first, propName)) {
            prop.prop = it->second.prop;
            propName = it->first;
            break;
        }
        if (it == --propMap.end()) {
            dprintf(fd, "Not found %s, please check!!!\n", propName.c_str());
            return;
        }
    }
    prop.timestamp = elapsedRealtimeNano();
    prop.status = VehiclePropertyStatus::AVAILABLE;

    // First pass: calculate sizes
    int sizeVal = 0;
    int stringIndex = 0;
    int areaIndex = 0;
    std::string valType;
    for (int i = 2, kv = 1; kv <= numberValues; kv++) {
        // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
        std::string type = options[i];
        std::string value = options[i + 1];
        if (EqualsIgnoreCase(type, "i") || EqualsIgnoreCase(type, "b") ||
            EqualsIgnoreCase(type, "l") || EqualsIgnoreCase(type, "f")) {
            sizeVal++;
            valType = type;
        } else if (EqualsIgnoreCase(type, "s")) {
            if (stringIndex != 0) {
                dprintf(fd,
                        "defining string value (%s) again at index %d (already defined at %d=%s"
                        ")\n",
                        value.c_str(), i, stringIndex, options[stringIndex + 1].c_str());
                return;
            }
            stringIndex = i;
        } else if (EqualsIgnoreCase(type, "a")) {
            if (areaIndex != 0) {
                dprintf(fd,
                        "defining area value (%s) again at index %d (already defined at %d=%s"
                        ")\n",
                        value.c_str(), i, areaIndex, options[areaIndex + 1].c_str());
                return;
            }
            areaIndex = i;
        } else {
            dprintf(fd, "invalid (%s) type at index %d\n", type.c_str(), i);
            return;
        }
        i += 2;
    }
    if (EqualsIgnoreCase(valType, "i")) {
        prop.value.int32Values.resize(sizeVal);
    } else if (EqualsIgnoreCase(valType, "b")) {
        prop.value.bytes.resize(sizeVal);
    } else if (EqualsIgnoreCase(valType, "l")) {
        prop.value.int64Values.resize(sizeVal);
    } else if (EqualsIgnoreCase(valType, "f")) {
        prop.value.floatValues.resize(sizeVal);
    }

    // Second pass: populate it
    int indexVal = 0;
    for (int i = 2, kv = 1; kv <= numberValues; kv++) {
        // iterate through the kv=1..n key/value pairs, accessing indexes i / i+1 at each step
        int valueIndex = i + 1;
        std::string type = options[i];
        std::string value = options[valueIndex];
        if (EqualsIgnoreCase(type, "i")) {
            int safeInt;
            if (!safelyParseInt(fd, valueIndex, value, &safeInt)) return;
            prop.value.int32Values[indexVal++] = safeInt;
        } else if (EqualsIgnoreCase(type, "b")) {
            uint8_t safeByte;
            if (!safelyParseByte(fd, valueIndex, value, &safeByte)) return;
            prop.value.bytes[indexVal++] = safeByte;
        } else if (EqualsIgnoreCase(type, "f")) {
            float safeFloat;
            if (!safelyParseFloat(fd, valueIndex, value, &safeFloat)) return;
            prop.value.floatValues[indexVal++] = safeFloat;
        } else if (EqualsIgnoreCase(type, "l")) {
            int64_t safeInt64;
            if (!safelyParseInt64(fd, valueIndex, value, &safeInt64)) return;
            prop.value.int64Values[indexVal++] = safeInt64;
        } else if (EqualsIgnoreCase(type, "s")) {
            prop.value.stringValue = value;
        } else if (EqualsIgnoreCase(type, "a")) {
            if (!safelyParseInt(fd, valueIndex, value, &prop.areaId)) return;
        }
        i += 2;
    }
    VHALOG.d("Setting prop %s", toString(prop).c_str());
    auto status = set(prop);
    if (status == StatusCode::OK) {
        dprintf(fd, "Set property %s successful!!!\n", toString(prop).c_str());
    } else {
        dprintf(fd, "Failed to set property %s: %s\n", propName.c_str(), toString(status).c_str());
    }
}

bool EmulatedVehicleHal::safelyParseInt(int fd, int index, std::string s, int* out) {
    if (!android::base::ParseInt(s, out)) {
        dprintf(fd, "non-integer argument at index %d: %s\n", index, s.c_str());
        return false;
    }
    return true;
}

bool EmulatedVehicleHal::safelyParseInt64(int fd, int index, std::string s, int64_t* out) {
    if (!android::base::ParseInt<int64_t>(s, out)) {
        dprintf(fd, "non-integer64 argument at index %d: %s\n", index, s.c_str());
        return false;
    }
    return true;
}

bool EmulatedVehicleHal::safelyParseByte(int fd, int index, std::string s, uint8_t* out) {
    if (!android::base::ParseUint<uint8_t>(s, out)) {
        dprintf(fd, "non-byte argument at index %d: %s\n", index, s.c_str());
        return false;
    }
    return true;
}

bool EmulatedVehicleHal::safelyParseFloat(int fd, int index, std::string s, float* out) {
    if (!android::base::ParseFloat(s, out)) {
        dprintf(fd, "non-float argument at index %d: %s\n", index, s.c_str());
        return false;
    }
    return true;
}

bool EmulatedVehicleHal::checkArgumentsSize(int fd, const hidl_vec<hidl_string>& options,
                                            size_t minSize) {
    size_t size = options.size();
    if (size >= minSize) {
        return true;
    }
    dprintf(fd, "Invalid number of arguments: required at least %zu, got %zu\n", minSize, size);
    return false;
}

StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    VHALOG.d("EmulatedVehicleHal.set 0x%x area:%d", propValue.prop, propValue.areaId);
    constexpr bool updateStatus = false;

    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        // Send the generator controlling request to the server.
        // 'updateStatus' flag is only for the value sent by setProperty (propValue in this case)
        // instead of the generated values triggered by it. 'propValue' works as a control signal
        // here, since we never send the control signal back, the value of 'updateStatus' flag
        // does not matter here.
        auto status = mVehicleClient->setProperty(propValue, updateStatus);
        return status;
    } else if (mHvacPowerProps.count(propValue.prop)) {
        auto hvacPowerOn = mPropStore->readValueOrNull(
            toInt(VehicleProperty::HVAC_POWER_ON),
            (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
             VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
             VehicleAreaSeat::ROW_2_RIGHT));

        if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1 &&
            hvacPowerOn->value.int32Values[0] == 0) {
            return StatusCode::NOT_AVAILABLE;
        }
    } else {
        // Handle property specific code
        switch (propValue.prop) {
            case OBD2_FREEZE_FRAME_CLEAR:
                return clearObd2FreezeFrames(propValue);
            case VEHICLE_MAP_SERVICE:
                // Placeholder for future implementation of VMS property in the default hal. For
                // now, just returns OK; otherwise, hal clients crash with property not supported.
                return StatusCode::OK;
        }
    }

    if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
        // Android side cannot set property status - this value is the
        // purview of the HAL implementation to reflect the state of
        // its underlying hardware
        return StatusCode::INVALID_ARG;
    }
    auto currentPropValue = mPropStore->readValueOrNull(propValue);

    if (currentPropValue == nullptr) {
        return StatusCode::INVALID_ARG;
    }
    if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
        // do not allow Android side to set() a disabled/error property
        return StatusCode::NOT_AVAILABLE;
    }

    /**
     * After checking all conditions, such as the property is available, a real vhal will
     * sent the events to Car ECU to take actions.
     */

    // Send the value to the vehicle server, the server will talk to the (real or emulated) car
    auto setValueStatus = mVehicleClient->setProperty(propValue, updateStatus);
    if (setValueStatus != StatusCode::OK) {
        return setValueStatus;
    }

    return StatusCode::OK;
}

static bool isDiagnosticProperty(VehiclePropConfig propConfig) {
    switch (propConfig.prop) {
        case OBD2_LIVE_FRAME:
        case OBD2_FREEZE_FRAME:
        case OBD2_FREEZE_FRAME_CLEAR:
        case OBD2_FREEZE_FRAME_INFO:
            return true;
    }
    return false;
}

// Parse supported properties list and generate vector of property values to hold current values.
void EmulatedVehicleHal::onCreate() {
    static constexpr bool shouldUpdateStatus = true;

    for (auto& it : kVehicleProperties) {
        VehiclePropConfig cfg = it.config;
        int32_t numAreas = cfg.areaConfigs.size();

        if (isDiagnosticProperty(cfg)) {
            // do not write an initial empty value for the diagnostic properties
            // as we will initialize those separately.
            continue;
        }

        // A global property will have only a single area
        if (isGlobalProp(cfg.prop)) {
            numAreas = 1;
        }

        for (int i = 0; i < numAreas; i++) {
            int32_t curArea;

            if (isGlobalProp(cfg.prop)) {
                curArea = 0;
            } else {
                curArea = cfg.areaConfigs[i].areaId;
            }

            // Create a separate instance for each individual zone
            VehiclePropValue prop = {
                .areaId = curArea,
                .prop = cfg.prop,
            };

            if (it.initialAreaValues.size() > 0) {
                auto valueForAreaIt = it.initialAreaValues.find(curArea);
                if (valueForAreaIt != it.initialAreaValues.end()) {
                    prop.value = valueForAreaIt->second;
                } else {
                    VHALOG.w("%s failed to get default value for prop 0x%x area 0x%x", __func__,
                          cfg.prop, curArea);
                }
            } else {
                prop.value = it.initialValue;
            }
            mPropStore->writeValue(prop, shouldUpdateStatus);
        }
    }
    auto configs = mVendorServer->onGetAllPropertyConfig();
    for (auto& it : configs) {
        VehiclePropConfig cfg = it.config;
        int32_t numAreas = cfg.areaConfigs.size();
        // A global property will have only a single area
        if (isGlobalProp(cfg.prop)) {
            numAreas = 1;
        }
        for (int i = 0; i < numAreas; i++) {
            int32_t curArea;

            if (isGlobalProp(cfg.prop)) {
                curArea = 0;
            } else {
                curArea = cfg.areaConfigs[i].areaId;
            }

            // Create a separate instance for each individual zone
            VehiclePropValue prop = {
                .areaId = curArea,
                .prop = cfg.prop,
                .status = cfg.changeMode == VehiclePropertyChangeMode::STATIC ? 
                    VehiclePropertyStatus::UNAVAILABLE:VehiclePropertyStatus::AVAILABLE
            };
            if (it.initialAreaValues.size() > 0) {
                auto valueForAreaIt = it.initialAreaValues.find(curArea);
                if (valueForAreaIt != it.initialAreaValues.end()) {
                    prop.value = valueForAreaIt->second;
                } else {
                    VHALOG.w("%s failed to get default value for prop 0x%x area 0x%x", __func__,
                          cfg.prop, curArea);
                }
            } else {
                prop.value = it.initialValue;
            }
            mPropStore->writeValue(prop, shouldUpdateStatus);
        }
    }
    initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
    initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
    mGarageModeHandler = makeGarageModeServerSideHandler((IVehicleServer*)mVehicleHalServer, getValuePool(),
                                                             std::string(POWER_STATE_MONITOR_PATH));
}

std::vector<VehiclePropConfig> EmulatedVehicleHal::listProperties() {
    return mPropStore->getAllConfigs();
}

void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
    VehiclePropValuePtr v;

    auto& pool = *getValuePool();

    for (int32_t property : properties) {
        if (isContinuousProperty(property)) {
            auto internalPropValue = mPropStore->readValueOrNull(property);
            if (internalPropValue != nullptr) {
                v = pool.obtain(*internalPropValue);
            }
        } else {
            VHALOG.e("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
        }

        if (v.get()) {
            v->timestamp = elapsedRealtimeNano();
            doHalEvent(std::move(v));
        }
    }
}

StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
    VHALOG.d("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);

    if (isContinuousProperty(property)) {
        mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
    }
    return StatusCode::OK;
}

StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
    VHALOG.d("%s propId: 0x%x", __func__, property);
    if (isContinuousProperty(property)) {
        mRecurrentTimer.unregisterRecurrentEvent(property);
    }
    return StatusCode::OK;
}

bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
    const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
    if (config == nullptr) {
        VHALOG.w("Config not found for property: 0x%x", propId);
        return false;
    }
    return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
}

bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
    constexpr bool updateStatus = true;
    return mVehicleClient->setProperty(propValue, updateStatus) == StatusCode::OK;
}

std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const {
    return mPropStore->readAllValues();
}

void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
    VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);

    if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
        getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue);
        doHalEvent(std::move(updatedPropValue));
    }
}

void EmulatedVehicleHal::initStaticConfig() {
    for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) {
        const auto& cfg = it->config;
        VehiclePropertyStore::TokenFunction tokenFunction = nullptr;

        switch (cfg.prop) {
            case OBD2_FREEZE_FRAME: {
                tokenFunction = [](const VehiclePropValue& propValue) {
                    return propValue.timestamp;
                };
                break;
            }
            default:
                break;
        }

        mPropStore->registerProperty(cfg, tokenFunction);
    }
}

void EmulatedVehicleHal::initObd2LiveFrame(const VehiclePropConfig& propConfig) {
    static constexpr bool shouldUpdateStatus = true;

    auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
    auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
                                            static_cast<size_t>(propConfig.configArray[1]));
    sensorStore->fillPropValue("", liveObd2Frame.get());
    liveObd2Frame->prop = OBD2_LIVE_FRAME;

    mPropStore->writeValue(*liveObd2Frame, shouldUpdateStatus);
}

void EmulatedVehicleHal::initObd2FreezeFrame(const VehiclePropConfig& propConfig) {
    static constexpr bool shouldUpdateStatus = true;

    auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
                                            static_cast<size_t>(propConfig.configArray[1]));

    static std::vector<std::string> sampleDtcs = {"P0070",
                                                  "P0102"
                                                  "P0123"};
    for (auto&& dtc : sampleDtcs) {
        auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
        sensorStore->fillPropValue(dtc, freezeFrame.get());
        freezeFrame->prop = OBD2_FREEZE_FRAME;

        mPropStore->writeValue(*freezeFrame, shouldUpdateStatus);
    }
}

StatusCode EmulatedVehicleHal::fillObd2FreezeFrame(const VehiclePropValue& requestedPropValue,
                                                   VehiclePropValue* outValue) {
    if (requestedPropValue.value.int64Values.size() != 1) {
        VHALOG.e("asked for OBD2_FREEZE_FRAME without valid timestamp");
        return StatusCode::INVALID_ARG;
    }
    auto timestamp = requestedPropValue.value.int64Values[0];
    auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
    if (freezeFrame == nullptr) {
        VHALOG.e("asked for OBD2_FREEZE_FRAME at invalid timestamp");
        return StatusCode::INVALID_ARG;
    }
    outValue->prop = OBD2_FREEZE_FRAME;
    outValue->value.int32Values = freezeFrame->value.int32Values;
    outValue->value.floatValues = freezeFrame->value.floatValues;
    outValue->value.bytes = freezeFrame->value.bytes;
    outValue->value.stringValue = freezeFrame->value.stringValue;
    outValue->timestamp = freezeFrame->timestamp;
    return StatusCode::OK;
}

StatusCode EmulatedVehicleHal::clearObd2FreezeFrames(const VehiclePropValue& propValue) {
    if (propValue.value.int64Values.size() == 0) {
        mPropStore->removeValuesForProperty(OBD2_FREEZE_FRAME);
        return StatusCode::OK;
    } else {
        for (int64_t timestamp : propValue.value.int64Values) {
            auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
            if (freezeFrame == nullptr) {
                VHALOG.e("asked for OBD2_FREEZE_FRAME at invalid timestamp");
                return StatusCode::INVALID_ARG;
            }
            mPropStore->removeValue(*freezeFrame);
        }
    }
    return StatusCode::OK;
}

StatusCode EmulatedVehicleHal::fillObd2DtcInfo(VehiclePropValue* outValue) {
    std::vector<int64_t> timestamps;
    for (const auto& freezeFrame : mPropStore->readValuesForProperty(OBD2_FREEZE_FRAME)) {
        timestamps.push_back(freezeFrame.timestamp);
    }
    outValue->value.int64Values = timestamps;
    outValue->prop = OBD2_FREEZE_FRAME_INFO;
    return StatusCode::OK;
}

}  // namespace impl

}  // namespace V2_0
}  // namespace vehicle
}  // namespace automotive
}  // namespace hardware
}  // namespace android
